Main function
toep_127()
Multiplies a Toeplitz matrix by a bit vector, returning the first 127 bits.const std::vector<uint64_t>&
First row and column of the Toeplitz matrix, packed as 64-bit words. Length should be
(lpn_t + 127 + 63) / 64 words.const std::vector<uint64_t>&
Input bit vector (LPN output), packed as 64-bit words. Length should be
(lpn_t + 63) / 64 words.uint64_t&
Output: lower 64 bits of the result
uint64_t&
Output: upper 63 bits of the result (most significant bit should be 0)
Algorithm
A Toeplitz matrix has constant diagonals:z = T * y is computed via GF(2) convolution:
- Perform binary polynomial multiplication:
R = top ⊗ ybits - Extract bits 0-126 from R as the output
This function performs runtime dispatch to choose between scalar, PCLMUL (x86), or PMULL (ARM) implementations based on hardware capabilities.
Implementation variants
toep_127_scalar()
Portable scalar implementation.gf2_conv_scalar() for binary polynomial multiplication without hardware acceleration.
toep_127_clmul()
Hardware-accelerated implementation using Intel PCLMUL.- x86-64 architecture
- PCLMUL instruction support
- Compile with
-mpclmulor-march=native
_mm_clmulepi64_si128 intrinsic for fast carryless multiplication.
toep_127_pmull()
Hardware-accelerated implementation for ARM NEON.- ARM64 (AArch64) architecture
- Crypto extensions
- Compile with
-march=armv8-a+crypto
vmull_p64 intrinsic for polynomial multiplication.
Binary polynomial multiplication
gf2_conv_scalar()
Scalar GF(2) convolution.const std::vector<uint64_t>&
First polynomial (bit-packed)
const std::vector<uint64_t>&
Second polynomial (bit-packed)
std::vector<uint64_t>&
Output: product polynomial with length
A.size() + B.size()R(x) = A(x) · B(x) in GF(2)[x] using the standard schoolbook algorithm:
- For each set bit at position
iin A - For each word in B
- XOR shifted B into R at position
i
gf2_conv_clmul()
PCLMUL-accelerated GF(2) convolution.gf2_conv_scalar(), but uses _mm_clmulepi64_si128 for 64×64→128 bit carryless multiplication.
gf2_conv_pmull()
PMULL-accelerated GF(2) convolution for ARM.gf2_conv_scalar(), but uses vmull_p64 for polynomial multiplication on ARM64.
Runtime selection
select_toeplitz()
Benchmarks available implementations and selects the fastest.toep_127(). It:
- Tests all available implementations (scalar, PCLMUL, PMULL)
- Benchmarks each on typical inputs
- Selects the fastest implementation
- Stores the selection in global variable
g_toep
The selection is cached in a global variable, so it only runs once per program execution.
Global variables
Toeplitz matrix properties
Structure
A Toeplitz matrix has the form:Universal hashing
Random Toeplitz matrices form a family of universal hash functions:- Uniformity: For random T and fixed x ≠ y, Pr[Tx = Ty] ≤ 2^(-127)
- Compression: Maps
lpn_tbits (16384) to 127 bits - Efficiency: Computable via fast convolution
Security role
In PVAC-HFHE, Toeplitz hashing serves as a randomness extractor:- LPN outputs
lpn_t = 16384bits with ~11000 bits min-entropy - Toeplitz extraction produces 127 nearly-uniform bits
- Result is hashed to a field element via
hash_to_fp_nonzero()
Performance characteristics
Benchmarks (approximate, varies by hardware)
Hardware acceleration provides ~7-10× speedup over scalar implementation.
Optimization notes
- The convolution is computed over
(lpn_t + 127)bits - Only the first 127 output bits are extracted
- Sparse inputs (few set bits) benefit from early termination
- Hardware implementations use SIMD parallelism
Example usage
Implementation details
Bit extraction
After convolution, the code extracts bits 0-126:Intrinsics used
x86-64 PCLMUL:Related functions
prf_R_core()- Usestoep_127()for randomness extractionlpn_make_ybits()- Generates the input to Toeplitz hashing